/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */


package ru.teleology.cubefs.editor.hex;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.tree.TreePath;

import ru.teleology.cubefs.api.models.Page;
import ru.teleology.cubefs.api.reflection.number.Number;
import ru.teleology.cubefs.api.reflection.Types;
import ru.teleology.cubefs.api.reflection.metadata.FieldMetadata;
import ru.teleology.cubefs.api.reflection.metadata.IDField;
import ru.teleology.cubefs.api.reflection.metadata.NumberField;
import ru.teleology.cubefs.api.reflection.metadata.PageMetadata.FieldDescriptor;
import ru.teleology.cubefs.api.reflection.value.TypedIDValue;
import ru.teleology.cubefs.editor.MainPanel;
import ru.teleology.cubefs.editor.model.ContainerTreeModel;
import ru.teleology.cubefs.editor.model.TreePageMetadataHelper;

public class HexEditorPanel extends JPanel {
	
	private final HexTable table = new HexTable(); 
	
	private JToggleButton edit = new JToggleButton("Edit");
	
	private JButton dec = new JButton("<<");
	private JButton inc = new JButton(">>");
	
	private static final int MAX_COLUMNS = 32;
	
	private JLabel details = new JLabel();

	private MainPanel navigation; 
	
	public HexEditorPanel() {
		this.setLayout(new BorderLayout());
		this.add(new JScrollPane(table), BorderLayout.CENTER);
		
		JPanel buttons = new JPanel();
		buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
		
		buttons.add(edit);
		
		buttons.add(dec);
		buttons.add(inc);
		
		JPanel status = new JPanel();
		status.setLayout(new FlowLayout(FlowLayout.LEFT));
		
		this.add(status, BorderLayout.SOUTH);
		
		status.add(details);
		
		details.setText(" ");
		
		edit.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				table.setEditMode(edit.isSelected());
			}			
		});
		
		dec.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				HexTableModel model = table.getHexModel();				
				int columns = model.getColumns();
				
				if (columns > 2) {
					columns--;
					model.setColumns(columns);
				}
			}
		});
		
		
		inc.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				HexTableModel model = table.getHexModel();
				int columns = model.getColumnCount() - 1;
				
				if (columns < MAX_COLUMNS) {
					columns++;
					model.setColumns(columns);
				}
			}
		});
		
		table.getHexModel().addTableModelListener(new TableModelListener(){
			public void tableChanged(TableModelEvent e) {
				dec.setEnabled(table.getHexModel().getColumns() > 2);
				inc.setEnabled(table.getHexModel().getColumns() < MAX_COLUMNS);
			}
		});
		
		ListSelectionListener fieldDescriptionListener = new ListSelectionListener(){
			public void valueChanged(ListSelectionEvent e) {
				int row = table.getSelectedRow();
				int col = table.getSelectedColumn();
				
				int idx = row * table.getHexModel().getColumns() + col - 1;
				
				Page page = table.getHexModel().getPage();
				if (page != null) {
					TreePageMetadataHelper metadata = table.getHexModel().getMetadata();
					FieldDescriptor field = metadata.getPage().getField(idx, false);
					if (field != null) {
						details.setText(getFieldText(page, metadata, field));
					}
					else {
						details.setText(" ");
					}
				}
			}			
		};
		
		table.getSelectionModel().addListSelectionListener(fieldDescriptionListener);
		table.getColumnModel().getSelectionModel().addListSelectionListener(fieldDescriptionListener);
		
		this.add(buttons, BorderLayout.NORTH);
		
		table.addFieldSelectionListener(new FieldSelectionListener() {
			public void fieldSelected(FieldSelectionEvent event) {
				FieldMetadata field = event.getField().getField();
				if (field instanceof IDField) {
					TypedIDValue id = (TypedIDValue) field.CreateValueHolder();
					((IDField) field).GetValue(event.getPage(), 0, id, false);
					
					if (!id.IsNull()) 
					{
						ContainerTreeModel treeModel = (ContainerTreeModel) navigation.getNavigation().getModel();
						Object[] path = treeModel.getPagePath(id);

						navigation.getNavigation().setSelectionPath(new TreePath(path));
						navigation.focusData();
					}
				}
				else if (field instanceof NumberField && "KEYS".equals(field.Name())) 
				{
					HexTableModel model = table.getHexModel(); 
					Page page = event.getPage();

					if (model.getMetadata().isLeaf(page)) 
					{
						Number value = (Number) field.CreateValueHolder();
						((NumberField) field).GetValue(event.getPage(), 0, value, false);

						ContainerTreeModel treeModel = (ContainerTreeModel) navigation.getNavigation().getModel();
						Object[] path = treeModel.getKeyPath(value, event.getPage().GetId());

						navigation.getNavigation().setSelectionPath(new TreePath(path));
						navigation.focusData();
					}
				}
			}	
		});
	}

	protected String getFieldText(Page page, TreePageMetadataHelper metadata,
			FieldDescriptor descr) {
	
		FieldMetadata field = descr.getField();
		
		StringBuilder sb = new StringBuilder();
		sb.append(field).append(" - ");
		
		if (field instanceof NumberField) 
		{
			NumberField nf = (NumberField) field;
			Number num = (Number) nf.CreateValueHolder();
			nf.GetValue(page, 0, num, false);
			
			long value = num.GetBigIntValue();
			int digits = getValueDigits(num);
			sb.append(num.toString()).append(" - 0x").append(HexHelper.asHex(value, digits));
		}
		else if (field instanceof IDField) {
			IDField idf = (IDField) field;
			
			TypedIDValue id = (TypedIDValue) idf.CreateValueHolder();
			
			idf.GetValue(page, 0, id, false);
			
			sb.append(id);
		}
		
		return sb.toString();
	}

	private int getValueDigits(Number num) {
		if (num.type() == Types.JByte) {
			return 2;
		}
		else if (num.type() == Types.JShort) {
			return 4;
		}
		else if (num.type() == Types.JChar) {
			return 4;
		}
		else if (num.type() == Types.JInt) {
			return 8;
		}
		else if (num.type() == Types.JLong) {
			return 16;
		}
		else {
			return 16;
		}
	}

	public HexTable getTable() {
		return table;
	}

	public void setNavigationTree(MainPanel navigation) {
		this.navigation = navigation;
	}

	public void focusTable() {
		table.requestFocusInWindow();
	}

	public void selectKeyField(int index) {
		HexTableModel model = table.getHexModel();
		NumberField field = model.getMetadata().getKeyFields().get(index);
		int ptr = field.Ptr();
		
		int row = ptr / model.getColumns();
		int col = ptr % model.getColumns() + 1;
		
		table.getSelectionModel().setSelectionInterval(row, row);
		table.getColumnModel().getSelectionModel().setSelectionInterval(col, col);
	}
}